package com.eastrobot.doc.poitlconvert.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.URLUtil;
import com.alibaba.fastjson.JSON;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.data.ChartMultiSeriesRenderData;
import com.deepoove.poi.data.PictureRenderData;
import com.deepoove.poi.data.PictureType;
import com.deepoove.poi.data.Pictures;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import com.deepoove.poi.render.compute.RenderDataComputeFactory;
import com.deepoove.poi.util.RegexUtils;
import com.eastrobot.doc.poitlconvert.entity.AcWordModel;
import com.eastrobot.doc.poitlconvert.entity.mytemplateEntity.AbstractMockData;
import com.eastrobot.doc.poitlconvert.entity.mytemplateEntity.MyTemplateData;
import com.eastrobot.doc.poitlconvert.mock.ArticleDataMock;
import com.eastrobot.doc.poitlconvert.mock.MyTemplateDataMock;
import com.eastrobot.doc.poitlconvert.service.PoitlService;
import com.eastrobot.doc.poitlconvert.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.ddr.poi.html.HtmlRenderPolicy;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.*;

@Slf4j
@Service
public class PoitlServiceImpl implements PoitlService {
    // PhontomJS 服务网址
    @Value("${phantomjs.url}")
    private String phantomjsUrl;
    /**
     * 根据id找到对应模版，并生成报告
     *
     * @param id
     * @param response
     */
    @Override
    public void getWordById(String id, HttpServletResponse response) {
        // 根据id获取模版
        String templatePath = getTemplate(id);
        // 根据id获取数据，此处使用mock数据，
        // 考虑动态化实现：实际可以从数据库查询、service方法或者api接口查询（根据实际业务来）
        MyTemplateData dataById = (MyTemplateData) getDataById(id);

        // 获取 Word 模板所在路径
        InputStream resourceAsStream = this.getClass().getClassLoader()
                .getResourceAsStream(templatePath);
        //这种写法支持打包成jar在linux服务器中使用能解决掉文件获取不到的问题
        try {
            //poi-tl写入报告

            //渲染数据
            XWPFTemplate xwpfTemplate = poitlDealAndRender(resourceAsStream,dataById, templatePath);

            String dateStr = DateFormatUtils.format(new Date(), "YYYYMMddHHmmss");
            // 将完成数据渲染的文档写出
            xwpfTemplate.writeAndClose(new FileOutputStream(
                    id + "Out-" + dateStr + ".docx"));

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                resourceAsStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

        }
        // 实际业务还需要保存生成报告记录
    }

    private XWPFTemplate poitlDealAndRender(InputStream resourceAsStream, MyTemplateData dataById, String templatePath) {
//        PictureRenderData pictureRenderData = Pictures.ofUrl("https://static.sitestack.cn/projects/poi-tl/3c5c8c270fcdaa55413b6b2a0cc03b1a.png")
//                .size(100, 100).create();
//        data.put("chart", pictureRenderData);

        PictureRenderData pictureRenderData = getEchartPic(dataById);
        dataById.setChart(pictureRenderData);

        // 给标签绑定插件，这里就绑定表格行循环的插件
        ConfigureBuilder configureBuilder = Configure.builder();
        configureBuilder.useSpringEL();
        configureBuilder.addPlugin('%',new CustomerLoopRowTableRenderPolicy());
        configureBuilder.setRenderDataComputeFactory((model) -> new CustomerDataCompute(model));

        LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
//        替换表格--模版四使用
//        configureBuilder.bind("chartDataModels", policy);
        configureBuilder.bind("chartDataModels", policy);
        CustomerLoopRowTableRenderPolicy customerLoopRowTableRenderPolicy = new CustomerLoopRowTableRenderPolicy();
        // 自定义表格项目
        configureBuilder.bind("dataList", customerLoopRowTableRenderPolicy);
        Configure configure = configureBuilder.build();


        // 通过 XWPFTemplate 编译文件并渲染数据到模板中
        try {
            Map<String, Object> dataByIdMap = BeanUtil.beanToMap(dataById);
            XWPFTemplate template = XWPFTemplate.compile(resourceAsStream, configure)
                    .render(dataByIdMap);
            return template;
        } catch (Exception e) {
            // 渲染表格失败
            log.error(e.getMessage(),e);
//            System.out.println(e.getMessage());
            throw new RuntimeException(e);

        }

    }

    private PictureRenderData getEchartPic(MyTemplateData dataById) {
        // java 图片
        // 数据参数，可以自己通过API查询json数据
        String title = "上海天气折线图";
        List<String> categories = Arrays.asList("2022-07-10", "2022-07-11", "2022-07-12", "2022-07-13", "2022-07-14", "2022-07-15", "2022-07-16", "2022-07-17", "2022-07-18", "2022-07-19", "2022-07-20", "2022-07-21", "2022-07-22");
        List<String> values = Arrays.asList("38", "33", "33", "31", "30", "32", "34", "37", "38", "37", "36", "38", "37");
        // 模板参数
        HashMap<String, Object> data = new HashMap<>();
        data.put("title", title);
        data.put("categories", JSON.toJSONString(categories));
        data.put("values", JSON.toJSONString(values));
        // 调用模板加载数据
        String option = FreemarkerUtil.generate("EChartsLineOption2.ftl", data);
        // 生成图片的base64编码
        String base64 = EChartsUtil.generateEChartsBase64(phantomjsUrl, option);
        // 将base64转为文件
        File file = Base64Util.base64ToFile(base64);
        PictureRenderData pictureRenderData = Pictures.ofLocal(file.getPath())
                .size(520, 500).create();
        return  pictureRenderData;
    }

    private AbstractMockData getDataById(String id) {

        // 根据id区分获取的数据
        MyTemplateData myTemplateData = MyTemplateDataMock.getMockDataById(id);
        return myTemplateData;
    }


    /**
     * 模拟模板获取，实际从数据库获取
     *
     * @param id
     * @return
     */
    private String getTemplate(String id) {

        String templatPath = "templates/doc/hello-world.docx";
        if (StringUtils.isNoneBlank(id)) {
            templatPath = "templates/doc/template" + id + ".docx";
        }
        ClassLoader classLoader = getClass().getClassLoader();
        URL resourceURL = classLoader.getResource(templatPath);
        String resourcePath = resourceURL.getPath();
        String decode = URLUtil.decode(resourcePath);
        File file = new File(decode);
        if (!file.isFile()) {
            throw new RuntimeException("当前id对应的模版不存在");
        }

        return templatPath;
    }


    @Override
    public void getWord4test(HttpServletResponse response) {
        Map<String, Object> data = new HashMap<>();
        data.put("title", "---做个不简单的国王很难");
        data.put("center", "这个是简单描述内容");
        //创建表格数据
        List<Map<String, Object>> goodsList = new ArrayList<>();
        Map<String, Object> d1 = new HashMap<String, Object>();
        d1.put("name", "手机");
        d1.put("goodsnum", "10");
        goodsList.add(d1);
        //渲染表格
//        HackLoopTableRenderPolicy policy = new HackLoopTableRenderPolicy();
        LoopRowTableRenderPolicy hackLoopTableRenderPolicy = new LoopRowTableRenderPolicy();

        //注意goodslist就是表格上定义的goodslist
        Configure config = Configure.newBuilder().bind("goodslist", hackLoopTableRenderPolicy).build();
        data.put("goodslist", goodsList);
        //本地测试这样写
//        ClassPathResource classPathResource = new ClassPathResource("/templates/test.docx");
//        String path = null;
//        try {
//            path = classPathResource.getURL().getPath();
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }
//        XWPFTemplate template = XWPFTemplate.compile(path,config).render(data);
        //渲染数据
        //这种写法支持打包成jar在linux服务器中使用能解决掉文件获取不到的问题
        XWPFTemplate template = XWPFTemplate.compile(this.getClass().getClassLoader()
                .getResourceAsStream("templates/doc/test2.docx"), config).render(data);
        WordUtil.downloadServlet(template, response);
    }

    @Override
    public void getWord4HellWord(HttpServletResponse response) {
        // 获取 Word 模板所在路径
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("templates/doc/hello-world.docx");
        // 给标签绑定插件
        Configure configure = Configure.builder().bind("author", new HtmlRenderPolicy()).build();
        // 通过 XWPFTemplate 编译文件并渲染数据到模板中
        XWPFTemplate template = XWPFTemplate.compile(resourceAsStream, configure).render(
                new HashMap<String, Object>() {{
                    put("title", "Hello, poi-tl Word模板引擎");
                    put("text", "Hello World");
                    put("author", "<h2>god23bin</h2>");
                    put("description", "做人民的子弟兵了！");
                }});
        try {
            // 将完成数据渲染的文档写出
            template.writeAndClose(new FileOutputStream("output.docx"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void getWord4Loop(HttpServletResponse response) {
        // 获取数据，这里假装是从数据库中查询得到的
        AcWordModel data = ArticleDataMock.getFromDB();
        // 获取 Word 模板所在路径
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("templates/doc/table-row-loop.docx");

//        String filepath = this.getClass().getClassLoader().getResource("templates/table-row-loop.docx").getPath();
        // 给标签绑定插件，这里就绑定表格行循环的插件
        Configure configure = Configure.builder()
                .bind("articles", new LoopRowTableRenderPolicy()/*new HackLoopTableRenderPolicy()*/)
                .bind("columns", new LoopRowTableRenderPolicy()/*new HackLoopTableRenderPolicy()*/)
                .build();
        // 通过 XWPFTemplate 编译文件并渲染数据到模板中
        XWPFTemplate template = XWPFTemplate.compile(resourceAsStream, configure).render(data);
        try {
            // 将完成数据渲染的文档写出
            template.writeAndClose(new FileOutputStream("table-row-loop-out.docx"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Template3 处理表达式
     *
     * @param response
     */
    @Override
    public void getExpressionWordOfTemplate3(HttpServletResponse response) {
        // 获取数据，这里假装是从数据库中查询得到的
        MyTemplateData data = MyTemplateDataMock.getFromDB();
        // 获取 Word 模板所在路径、
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("templates/doc/03text.docx");

        // 用来渲染数据的 Map，key 为模板中可选文字指定的值，value 为图表模型对象
        Map<String, Object> dataMap = new HashMap<>();

//        dataMap.put("ZJZYC", data.getZJZYC());
//        dataMap.put("MXYC", data.getMXYC());
//        dataMap.put("LNGXBGXY01", data.getLNGXBGXY01());
//        dataMap.put("ssss", "ww");

        RenderDataComputeFactory renderDataComputeFactory = model -> el -> {
//            String eval = FelUtil.eval(el,data);
//            String eval = QlExpressUtil.eval(el,data);
//            String eval = AviatorUtil.eval(el,data);
            Object eval = MvelUtil.eval(el, BeanUtil.beanToMap(data));
            return eval;
        };

        // 给标签绑定插件，这里就绑定表格行循环的插件
        ConfigureBuilder configureBuilder = Configure.builder();
        configureBuilder.buildGrammerRegex(RegexUtils.createGeneral("{{", "}}"));

//                .setRenderDataComputeFactory(new RenderDataComputeExpression())
//        configureBuilder.useSpringEL();
        configureBuilder.setRenderDataComputeFactory(renderDataComputeFactory);

        Configure configure = configureBuilder.build();

        // 通过 XWPFTemplate 编译文件并渲染数据到模板中
        XWPFTemplate template = XWPFTemplate.compile(resourceAsStream, configure).render(dataMap);
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(DateFormatUtils.format(new Date(), "YYYYMMddHHmmss") + "03textOut.docx");
            // 将完成数据渲染的文档写出
            template.writeAndClose(out);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                resourceAsStream.close();
                out.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * Template2 生成echarts
     *
     * @param response
     */
    @Override
    public void getChartWordOfTemplate2(HttpServletResponse response) {
        // 获取数据，这里假装是从数据库中查询得到的
//        MyTemplateData data = MyTemplateDataMock.getFromDB();
        // 获取 Word 模板所在路径
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("templates/doc/02chart.docx");

        // 用来渲染数据的 Map，key 为模板中可选文字指定的值，value 为图表模型对象
        Map<String, Object> dataMap = new HashMap<>();
        ChartMultiSeriesRenderData chart = ChartUtil.dealChart();
        dataMap.put("barChart", chart);
        Configure configure = Configure.builder()
                .build();

        // 通过 XWPFTemplate 编译文件并渲染数据到模板中
        XWPFTemplate template = XWPFTemplate.compile(resourceAsStream, configure).render(dataMap);
        try {
            // 将完成数据渲染的文档写出
            template.writeAndClose(new FileOutputStream(DateFormatUtils.format(new Date(), "YYYYMMddHHmmss") + "02chartOut.docx"));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                resourceAsStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }


}
